package com.dc.commons.config.shiro;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Lazy;
import org.springframework.util.StringUtils;

import com.dc.commons.jpa.TicketWapper;
import com.dc.commons.shiro.ShiroTicketWapper;
import com.dc.commons.shiro.credential.DcHashedCredentialsMatcher;
import com.dc.commons.shiro.filter.DcFormAuthenticationFilter;
import com.dc.commons.shiro.listener.DcUserListener;
import com.dc.commons.shiro.realm.SimpleRealm;
import com.dc.commons.shiro.session.DcSessionFactory;

/*******************************************************************************<P>
 * 文件名: ShiroConfig.java <br>
 * 版本: 1.0<br>
 * 描述: Shiro 配置<br>
 * 版权所有: <br>
 * <P>
 * 创建者: 王建栋 <br>
 * 创建日期: 2015年12月17日 上午9:56:04<br>
 * 修改者: <br>
 * 修改日期: <br>
 * 修改说明: <br>
 *****************************************************************************
 */
@Configuration
@Lazy(false)
public class ShiroConfig {
	
	/**
	 * 缓存文件地址
	 */
	@Value("${shiro.cacheManager.configFile}")
	private String cacheManagerConfigFile;
	/**
	 * cookieId
	 */
	@Value("${shiro.cookieId}")
	private String cookieId;
	
	/**
	 * cookie 时间 -1 为关闭浏览器即失效 单位 秒
	 */
	@Value("${shiro.cookieMaxAge}")
	private int cookieMaxAge;
	
	/**
	 * rememberMe
	 */
	@Value("${shiro.rememberMe}")
	private String rememberMe;
	
	/**
	 * rememberMe 最大有效时长 单位秒
	 */
	@Value("${shiro.rememberMaxAge}")
	private int rememberMaxAge;
	/**
	 * remember 加密密钥
	 */
	@Value("${shiro.remember.cipherKey}")
	private String rememberCipherKey;
	
	/**
	 * 当前Session缓存名称
	 */
	@Value("${shiro.activeSessionsCacheName}")
	private String activeSessionsCacheName;
	
	/**
	 * session 超时时间 单位 秒
	 */
	@Value("${shiro.globalSessionTimeout}")
	private int globalSessionTimeout;
	
	/**
	 * 是否删除过期的Session
	 */
	@Value("${shiro.deleteInvalidSessions}")
	private boolean deleteInvalidSessions;
	
	/**
	 * 是否启动Sessioncookie
	 */
	@Value("${shiro.sessionIdCookieEnabled}")
	private boolean sessionIdCookieEnabled;
	
	/**
	 * Session 调度时间间隔
	 */
	@Value("${shiro.sessionValidationInterval}")
	private int sessionValidationInterval;
	
	/**
	 * 密码加密算法 支持 MD2 MD5 SHA SHA1 SHA-1 SHA-224 SHA-256 SHA-384 SHA-512
	 */
	@Value("${shiro.hashAlgorithmName}")
	private String hashAlgorithmName;
	
	/**
	 * 密码加密次数
	 */
	@Value("${shiro.hashIterations}")
	private int hashIterations;
	
	/**
	 * 登录 url
	 */
	@Value("${shiro.loginUrl}")
	private String loginUrl;
	
	/**
	 * 登录成功URL
	 */
	@Value("${shiro.successUrl}")
	private String successUrl;
	
	/**
	 * 退出重定向URL
	 */
	@Value("${shiro.redirectUrl}")
	private String redirectUrl;
	
	/**
	 * 未授权重定向URL
	 */
	@Value("${shiro.unauthorizedUrl}")
	private String unauthorizedUrl;
	
	/**
	 * 登录名称
	 */
	@Value("${shiro.usernameParam}")
	private String usernameParam;
	
	/**
	 * 密码名称
	 */
	@Value("${shiro.passwordParam}")
	private String passwordParam;
	
	/**
	 * 记住我名称
	 */
	@Value("${shiro.rememberMeParam}")
	private String rememberMeParam;
	
	/**
	 * 权限定义
	 */
	@Value("${shiro.filterChainDefinition}")
	private String filterChainDefinition;
	
	
	/**
	 *  缓存
	 * @return manager
	 */
	@Bean
	public CacheManager shiroCacheManager(){
		EhCacheManager manager=new EhCacheManager();
		manager.setCacheManagerConfigFile(cacheManagerConfigFile);
		return manager;
	}
	
	/**
	 * 会话生成器
	 * @return sessionIdGenerator
	 */
	@Bean
	public JavaUuidSessionIdGenerator sessionIdGenerator(){
		return new JavaUuidSessionIdGenerator();
	}
	
	@Bean
	@SuppressWarnings("rawtypes")
	public List dcUserListeners(){
		List<DcUserListener> list=new ArrayList<DcUserListener>();
		list.add(new DcUserListener(sessionDao()));
		return list;
	}
	
	/**
	 * 会话Cookie模板
	 * @return sessionIdCookie
	 */
	@Bean
	public SimpleCookie sessionIdCookie(){
		SimpleCookie cookie=new SimpleCookie(cookieId);
		cookie.setHttpOnly(true);
		cookie.setMaxAge(cookieMaxAge);
		return cookie;
	}
	
	/**
	 * 记住我 cooki
	 * @return rememberMeCookie
	 */
	@Bean
	public SimpleCookie rememberMeCookie(){
		SimpleCookie cookie=new SimpleCookie(rememberMe);
		cookie.setHttpOnly(true);
		cookie.setMaxAge(rememberMaxAge);
		return cookie;
	}
	
	/**
	 * rememberMe管理器
	 * @return rememberMeManager
	 */
	@Bean
	public CookieRememberMeManager rememberMeManager(){
		CookieRememberMeManager manager=new CookieRememberMeManager();
		manager.setCookie(rememberMeCookie());
		manager.setCipherKey(Base64.decode(rememberCipherKey));
		return manager;
	} 
	
	/**
	 * 会话DAO
	 * @return sessionDao
	 */
	@Bean
	public EnterpriseCacheSessionDAO sessionDao(){
		EnterpriseCacheSessionDAO dao=new EnterpriseCacheSessionDAO();
		dao.setSessionIdGenerator(sessionIdGenerator());
		dao.setActiveSessionsCacheName(activeSessionsCacheName);
		//需要自定义实现缓存否则会使用内存缓存
		dao.setCacheManager(shiroCacheManager());
		return dao;
	}
	
	/**
	 * Session 工厂
	 * @return sessionFactory
	 */
	@Bean
	public DcSessionFactory sessionFactory(){
		return new DcSessionFactory();
	}
	
	/**
	 * 会话管理器
	 * @return sessionManager
	 */
	@SuppressWarnings("unchecked")
	@Bean
	public DefaultWebSessionManager sessionManager(){
		DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();
		sessionManager.setGlobalSessionTimeout(globalSessionTimeout*1000);//超时时间 单位毫秒
		sessionManager.setDeleteInvalidSessions(deleteInvalidSessions);// 是否删除超时session
		//以下部分调整到 会话调度器 
		//sessionManager.setSessionValidationSchedulerEnabled(true);//是否启动定时
		//sessionManager.setSessionValidationScheduler(sessionValidationScheduler());// 这里可能会出现问题
		sessionManager.setSessionDAO(sessionDao());
		sessionManager.setSessionIdCookieEnabled(sessionIdCookieEnabled);
		sessionManager.setSessionIdCookie(sessionIdCookie());
		sessionManager.setSessionListeners(dcUserListeners());// 添加监控 ，主要是监控那些过期会话
		sessionManager.setSessionFactory(sessionFactory());// 可以自定义Session 添加一些字段 例如被踢的时候 添加一些通知之类的
		
		return sessionManager;
	}
	
	/**
	 * 会话调度器
	 * @return sessionValidationScheduler
	 */
	@Bean
	public QuartzSessionValidationScheduler sessionValidationScheduler(){
		QuartzSessionValidationScheduler scheduler=new QuartzSessionValidationScheduler();
		scheduler.setSessionValidationInterval(sessionValidationInterval*1000);// 调用时间价格 30分钟  单位毫秒
		DefaultWebSessionManager sessionManager=sessionManager();
		sessionManager.setSessionValidationSchedulerEnabled(true);//是否定时启动
		sessionManager.setSessionValidationScheduler(scheduler);
		scheduler.setSessionManager(sessionManager);
		return scheduler;
	}
	
	
	/**
	 * Ream 实现
	 * #TODO 可以自由的切换
	 * @return simpleRealm
	 */
	
	@Bean
	public SimpleRealm simpleRealm(){
		SimpleRealm realm=new SimpleRealm();
		realm.setCachingEnabled(false);
		realm.setCredentialsMatcher(credentialsMatcher());
		realm.setSessionDao(sessionDao());
		return realm;
	}
	
	/**
	 * Shiro securityManager 配置
	 * @return securityManager
	 */
	@SuppressWarnings("unchecked")
	@Bean
	public DefaultWebSecurityManager securityManager(){
		DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
		ModularRealmAuthenticator authenticator=new ModularRealmAuthenticator();
		authenticator.setAuthenticationListeners(dcUserListeners());
		securityManager.setAuthenticator(authenticator); //可以通过这个注册 监听，监听用户的登录与退出。
		securityManager.setRealm(simpleRealm());
		securityManager.setSessionManager(sessionManager());
		securityManager.setRememberMeManager(rememberMeManager());
		securityManager.setCacheManager(shiroCacheManager());
		SecurityUtils.setSecurityManager(securityManager);
		return securityManager;
	}
	
	
	
	/**
	 * 凭证匹配器 
	 * @return credentialsMatcher
	 */
	@SuppressWarnings("unchecked")
	@Bean
	public HashedCredentialsMatcher credentialsMatcher(){
		DcHashedCredentialsMatcher matcher=new DcHashedCredentialsMatcher(shiroCacheManager());
		matcher.setSessionDao(sessionDao());
		matcher.setDcSessionListeners(dcUserListeners());
		matcher.setHashAlgorithmName(hashAlgorithmName);
		matcher.setHashIterations(hashIterations);
		matcher.setStoredCredentialsHexEncoded(true);
		return matcher;
	}
	
	
	/**
	 * 登录过滤器扩展及配置
	 * @return formAuthenticationFilter
	 */
	@Bean
	public FormAuthenticationFilter formAuthenticationFilter(){
		DcFormAuthenticationFilter filter=new DcFormAuthenticationFilter();
		filter.setLoginUrl(loginUrl);
		filter.setUsernameParam(usernameParam);
		filter.setPasswordParam(passwordParam);
		filter.setRememberMeParam(rememberMeParam);
		filter.setSessionDao(sessionDao());
		return filter;
	}
	
	/**
	 * 退出过滤器配置
	 * @return logoutFilter
	 */
	@Bean
	public LogoutFilter logoutFilter(){
		LogoutFilter filter=new LogoutFilter();
		filter.setRedirectUrl(redirectUrl);
		return filter;
	}
	
	/**
	 * Shiro 过滤器配置
	 * @return shiroFilter
	 */
	@Bean(name="shiroFilter")
	public ShiroFilterFactoryBean shiroFilter(){
		ShiroFilterFactoryBean factory=new ShiroFilterFactoryBean();
		factory.setSecurityManager(securityManager());
		factory.setLoginUrl(loginUrl);
		factory.setSuccessUrl(successUrl);
		factory.setUnauthorizedUrl(unauthorizedUrl);
		
		Map<String,Filter> filterMap=new HashMap<String,Filter>();
		filterMap.put("authc", formAuthenticationFilter());
		filterMap.put("logout", logoutFilter());
		factory.setFilters(filterMap);
		
		if(StringUtils.isEmpty(filterChainDefinition)){
			Map<String,String> filterChainDefinitionMap=new LinkedHashMap<String,String>();
			filterChainDefinitionMap.put("/main/login.htm", "authc");
			filterChainDefinitionMap.put("/main/logout.htm", "logout");
			filterChainDefinitionMap.put("/main/authenticated.htm", "authc");
			factory.setFilterChainDefinitionMap(filterChainDefinitionMap);
		}else{
			factory.setFilterChainDefinitions(filterChainDefinition);
		}
		return factory;
	}
	
	
	
	
	/**
	 * AOP式方法级权限检查 
	 * @return defaultAdvisorAutoProxyCreator
	 */
	@Bean
	@DependsOn("lifecycleBeanPostProcessor")
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
		DefaultAdvisorAutoProxyCreator creator= new DefaultAdvisorAutoProxyCreator();
		creator.setProxyTargetClass(true);
		return creator;
	}
	
	/**
	 * 注解拦截配置
	 * @return authorizationAttributeSourceAdvisor
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
		AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
		advisor.setSecurityManager(securityManager());
		return advisor;
	}
	
	/**
	 * 当前用户信息提供者
	 * @return ticketWapper
	 */
	@Bean
	public TicketWapper ticketWapper(){
		return new ShiroTicketWapper();
	}
	
}
